package com.landun.market.web.controller;

import com.github.pagehelper.PageHelper;
import com.landun.market.exception.BusinessException;
import com.landun.market.pojo.enums.ResponseCode;
import com.landun.market.pojo.enums.StateCode;
import com.landun.market.pojo.model.User;
import com.landun.market.service.IUserService;
import com.landun.market.util.CollectionUtils;
import com.landun.market.util.NumberUtils;
import com.landun.market.web.PageableEntitiesConverter;
import com.landun.market.web.domain.ApiOut;
import com.landun.market.web.domain.LoginUser;
import com.landun.market.web.domain.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author jiang.chuan
 * @date 2018-05-12
 */
@Slf4j
@RestController
@RequestMapping("/users")
public class UserController extends AbstractController {
    private final IUserService userService;

    public UserController(IUserService userService) {
        this.userService = userService;
    }

    /**
     * 查询所有用户列表
     *
     * @param keyword   查询的关键字
     * @param pageIndex 当前页码
     * @param pageSize  每页显示品类条数
     * @return 品类列表
     */
    @GetMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<List<UserVO>> getAll(
            @RequestParam(name = "q", required = false) String keyword,
            @RequestParam(name = "pageIndex", required = false, defaultValue = "1") Integer pageIndex,
            @RequestParam(name = "pageSize", required = false, defaultValue = "10") Integer pageSize) {

        // MyBatis 自动分页
        PageHelper.startPage(pageIndex, pageSize);

        // 按统一格式返回数据
        return new ApiOut.Builder<List<UserVO>>(true)
                .data(PageableEntitiesConverter.toVos(userService.find(keyword), this::toVos))
                .build();
    }

    /**
     * 查询登陆人详情
     *
     * @return 用户详情
     */
    @GetMapping(path = "/self", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<UserVO> getSelf() {
        return new ApiOut.Builder<UserVO>(true).data(toVo(super.getUser())).build();
    }

    /**
     * 查询用户详情
     *
     * @param userId 用户ID
     * @return 用户详情
     */
    @GetMapping(path = "/{userId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<UserVO> getDetail(@PathVariable("userId") Integer userId) {
        if (NumberUtils.isNotPositive(userId)) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "用户ID");
        }

        // 按统一格式返回数据
        return new ApiOut.Builder<UserVO>(true).data(toVo(userService.load(userId))).build();
    }


    /**
     * 检查名字是否重复
     *
     * @param identifier 品类名
     * @param userId     需要排除的品类ID
     * @return true: 用户已经存在，false: 用户不存在
     */
    @GetMapping(path = "/duplicate/{identifier}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<Boolean> isDuplicated(
            @PathVariable("identifier") String identifier,
            @RequestParam(name = "id", required = false) Integer userId) {

        identifier = decodeString(identifier);
        if (StringUtils.isBlank(identifier)) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "用户标识");
        }

        return new ApiOut.Builder<Boolean>(true)
                .data(userService.isDuplicated(identifier, userId))
                .build();
    }


    /**
     * 新建用户
     *
     * @param user 用户
     * @return true: 创建成功，false:创建失败
     */
    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<Boolean> create(@RequestBody UserVO user) {
        //  校验证参数
        if (null == user) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "用户");
        }

        // 账号 / 姓名 / 电话 / 所在公司
        if (StringUtils.isBlank(user.getLoginName())) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "登陆账号");
        }

        // 密码
        if (StringUtils.isBlank(user.getPassword()) || user.getPassword().length() < 6) {
            throw new BusinessException(ResponseCode.PARAMETER_FORMAT_ERROR, "密码");
        }

        // 保存数据
        return userService.create(user.toEntity())
                ? new ApiOut.Builder<Boolean>().code(ResponseCode.SUCCESS).data(true).build()
                : new ApiOut.Builder<Boolean>().code(ResponseCode.CREATE_FAILED).message("保存用户失败").data(false).build();
    }

    /**
     * 更新用户信息
     *
     * @param userId 用户ID
     * @param user   用户
     * @return true:更新成功，false:更新失败
     */
    @PutMapping(path = "/{userId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<Boolean> update(@PathVariable("userId") Integer userId, @RequestBody UserVO user) {
        //  校验证参数
        if (NumberUtils.isNotPositive(userId)) {
            throw new BusinessException(ResponseCode.PARAMETER_FORMAT_ERROR, "用户ID");
        }

        if (null == user) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "用户");
        }

        // 只能修改：姓名 / 电话 / 所在公司
        if (StringUtils.isAllBlank(user.getName(), user.getNickname(), user.getEmail(), user.getMobile(), user.getCompany())) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "可修改的信息");
        }

        // 保存数据
        user.setId(userId);
        return userService.update(user.getId(), user.toEntity())
                ? new ApiOut.Builder<Boolean>().code(ResponseCode.SUCCESS).data(true).build()
                : new ApiOut.Builder<Boolean>().code(ResponseCode.UPDATE_FAILED).message("更新用户失败").data(false).build();
    }

    /**
     * 修改自己的密码
     *
     * @param loginUser 用户信息
     * @return 生成的密码
     */
    @PutMapping(path = "/change-password", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<String> changePassword(@RequestBody LoginUser loginUser) {
        // 如果指定了密码，就不能太简单
        if (null == loginUser || (StringUtils.isNotBlank(loginUser.getPassword()) && loginUser.getPassword().length() < 6)) {
            throw new BusinessException(ResponseCode.PARAMETER_FORMAT_ERROR, "密码");
        }

        if (StringUtils.isBlank(loginUser.getOldPassword())) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "旧密码");
        }

        // 如果没有传密码，则随机生成
        String password = getPassword(loginUser);
        return userService.changePassword(super.getUser().getId(), loginUser.getOldPassword(), password)
                ? new ApiOut.Builder<String>().data(password).build()
                : new ApiOut.Builder<String>().code(ResponseCode.DATA_NOT_UPDATED).build();
    }

    /**
     * 修改指定用户的密码
     *
     * @param userId    用户ID
     * @param loginUser 用户信息
     * @return 生成的密码
     */
    @PutMapping(path = "/change-password/{userId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<String> changePassword(@PathVariable("userId") Integer userId, @RequestBody LoginUser loginUser) {
        if (NumberUtils.isNotPositive(userId)) {
            throw new BusinessException(ResponseCode.PARAMETER_FORMAT_ERROR, "用户ID");
        }

        // 如果指定了密码，就不能太简单
        if (null == loginUser || (StringUtils.isNotBlank(loginUser.getPassword()) && loginUser.getPassword().length() < 6)) {
            throw new BusinessException(ResponseCode.PARAMETER_FORMAT_ERROR, "密码");
        }

        // 如果没有传密码，则随机生成
        String password = getPassword(loginUser);
        return userService.changePassword(userId, null, password)
                ? new ApiOut.Builder<String>().data(password).build()
                : new ApiOut.Builder<String>().code(ResponseCode.DATA_NOT_UPDATED).build();
    }

    /**
     * 禁用用户
     *
     * @param userId 用户ID
     * @return true:禁用成功，false:禁用失败
     */
    @PutMapping(path = "/disable/{userId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<Boolean> disable(@PathVariable("userId") Integer userId) {
        // 校验证参数
        if (NumberUtils.isNotPositive(userId)) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "用户ID");
        }

        // 禁用用户
        return userService.changeState(userId, StateCode.DISABLED)
                ? new ApiOut.Builder<Boolean>().code(ResponseCode.SUCCESS).data(true).build()
                : new ApiOut.Builder<Boolean>().code(ResponseCode.DISABLE_FAILED)
                .message("禁用用户" + userId + "失败").data(false).build();
    }


    /**
     * 启用用户
     *
     * @param userId 用户ID
     * @return true:启用成功，false:启用失败
     */
    @PutMapping(path = "/enable/{userId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ApiOut<Boolean> enable(@PathVariable("userId") Integer userId) {
        // 校验证参数
        if (NumberUtils.isNotPositive(userId)) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "用户ID");
        }

        // 启用用户
        return userService.changeState(userId, StateCode.ACTIVE)
                ? new ApiOut.Builder<Boolean>().code(ResponseCode.SUCCESS).data(true).build()
                : new ApiOut.Builder<Boolean>().code(ResponseCode.ENABLE_FAILED)
                .message("启用用户" + userId + "失败").data(false).build();
    }

    // ======================== Private Method ========================

    /**
     * 如果没有指定密码，则随机生成8位密码
     *
     * @param loginUser 用户传入密码
     * @return 密码串
     */
    private String getPassword(LoginUser loginUser) {
        if (null == loginUser || StringUtils.isBlank(loginUser.getPassword())) {
            return RandomStringUtils.randomAlphanumeric(8);
        }
        return loginUser.getPassword();
    }

    private UserVO toVo(User user) {
        return Optional.ofNullable(user).map(UserVO::new).orElse(null);
    }

    private List<UserVO> toVos(List<User> users) {
        if (CollectionUtils.isEmpty(users)) {
            return new ArrayList<>(0);
        }
        return users.stream().map(this::toVo).filter(Objects::nonNull).collect(Collectors.toList());
    }
}
